#include "real-page-predictor.h"
#include <string.h>
#include "readcache-with-LRU.h"


//所以我们一行行读出来，然后使用逗号分隔开
//将块大小设定为4K
#define PAGE_SIZE 1024

//每一行的最大字节数，同时也是行缓冲区的大小
#define MAX_LINE_SIZE 1024

//预测深度的最大值
#define MAX_PREDICT_DEEP 20

//初始化
void init_page_predictor(page_predictor_t* page_predictor){
    page_predictor->last_access = -1;
    page_predictor->predictor_size = 2;
}

//这里进行预测
void real_page_predictor(page_predictor_t* page_predictor, long now_access, long *predictor_arr, int *size){
    if(predictor_arr == NULL){
        printf("predictor_arr必须在外部分配好空间、\n");
        return;
    }
    printf("开始进行page顺序预测，now_access = %ld\n", now_access);

    int i;

    if(page_predictor->last_access + 1 == now_access){
        if(page_predictor->predictor_size < 2){
            page_predictor->predictor_size = page_predictor->predictor_size * 2;
        }

        *size = page_predictor->predictor_size;
    }else{
        page_predictor->predictor_size = 2;
        *size = 0;
    }

    for(i = 0; i < *size; i++){
        //开始检查上一次预测的记录
        predictor_arr[i] = now_access + i + 1;
    }

    page_predictor->last_access = now_access;
}

//这里是main函数
int main(void){
    //从文件中一个个读入
    FILE *fp_read = NULL;
    char line[MAX_LINE_SIZE];
    long access_block_count;
    long* predict_seq;

    int size;
    int i;

    //这里加入一个读缓存
    all_read_cache_t read_caches;
    //初始化读缓存
    init_readcache_meta(&read_caches);

    //初始化一个函数来看看缓存命中率
    long all_count_of_block = 0;
    long hit_count_of_block = 0;
    long noah_predict_time = 0;
    long stable_predictor_time = 0;

    //初始化一个预测序列
    predict_seq = (long*)malloc(MAX_PREDICT_DEEP*sizeof(long));
    
    printf("开始运行预测\n");

    if((fp_read = fopen("resource/block_count_hm.csv", "r"))!=NULL){
        printf("开始读取访问的历史记录\n");
        
        //这里创建一个数组来存储
        page_predictor_t page_predictor;
        init_page_predictor(&page_predictor);

        while(fgets(line, MAX_LINE_SIZE, fp_read)){
            //记录读进来的块
            all_count_of_block++;

            //将一行的内容转化为块号传入
            access_block_count = atol(line);

            //看看这个块是不是命中的
            //哈希一下
            int barrel_num = access_block_count % MAX_READ_CACHE_NUM;
            if(access_read_cache(&(read_caches.read_cache_arr[barrel_num]), access_block_count) != NULL){
                hit_count_of_block++;
            }
            
            //在外部将传入的预测序列初始化
            memset(predict_seq, 0, MAX_PREDICT_DEEP*sizeof(long));

            real_page_predictor(&page_predictor, access_block_count, predict_seq, &size);

            //如果是新读入缓存的也要记一次访问
            add_cache_meta(&read_caches, access_block_count);
            access_read_cache(&(read_caches.read_cache_arr[barrel_num]), access_block_count);
            
            //这里看看预测成功率
            
            //这里可以单纯使用page的预测方法
            for(i = 0; i < size; i++){
                add_cache_meta(&read_caches, predict_seq[i]);
            }
            // for(i = 0;  i < size2 && i < size; i++){
            //     //当两个预测器预测地一样的时候才会写入
            //     if(predict_seq[i] == predict_seq2[i]){
            //         add_cache_meta(&read_caches, predict_seq2[i]);
            //         // if(i == 0){
            //         //     update_and_judge_mode(&mode_judger, predict_seq[i]);
            //         // }
            //     }else if(i < size3 && predict_seq2[i] == predict_seq3[i]){
            //         add_cache_meta(&read_caches, predict_seq3[i]);
            //     }
            // }

            // for(i = 0;  i < size; i++){
            //     //当两个预测器预测地一样的时候才会写入
            //     add_cache_meta(&read_caches, predict_seq[i]);    
            // }

            //打印一下预测到的东西
            // printf("预测:");
            // for(i = 0;  i < size; i++){
            //     printf(" %ld", predict_seq[i]);
            // }

            // printf("\n");
            
        }
        
    }else{
        printf("文件开启失败\n");
        return 0;
    }

    //关闭
    printf("读取完毕\n");
    fclose(fp_read);
    fp_read = NULL;
    
    //计算缓存命中率
    
    int hit_rate = hit_count_of_block  * 100 / all_count_of_block;

    //预测错误率
    //这里整理一下预测错误率
    long err_cache;
    for(int i = 0 ; i < MAX_READ_CACHE_NUM; i++){
        err_cache = err_cache + read_caches.read_cache_arr[i].page_cache_have_not_access;

        //将已有的数据拿出做一个分析计数
        read_cache_t* each_read_cache = &(read_caches.read_cache_arr[i]);

        read_cache_meta_t* scan_item = each_read_cache->head->next;

        while(scan_item != each_read_cache->head){
            if(scan_item->have_accessed == 0){
                err_cache++;
            }
            scan_item = scan_item->next;
        }
    }
    long all_del_cache = 0;
    for(int i = 0 ; i < MAX_READ_CACHE_NUM; i++){
        all_del_cache = all_del_cache + read_caches.read_cache_arr[i].page_cache_del;

        read_cache_t* each_read_cache = &(read_caches.read_cache_arr[i]);

        read_cache_meta_t* scan_item = each_read_cache->head->next;

        while(scan_item != each_read_cache->head){
            
            all_del_cache++;
            
            scan_item = scan_item->next;
        }
    }

    printf("所有弹出的缓存%ld\n", all_del_cache);
    printf("错误的次数%ld\n", err_cache);
    
    
    printf("总访问次数%ld\n", all_count_of_block);
    printf("缓冲命中率:%d%%\n", hit_rate);

    int err_rate = err_cache * 100 / all_del_cache;
    
    
    
    printf("预测错误率:%d%%\n", err_rate);
}